/*
 * Copyright (C) 2008  Wang Hoi <fearee@gmail.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  US
*/
#include "memcache.h"
#include <QtCore>
#include <QtNetwork>
#include <memcached.h>
using namespace MemCachePrivate;
class MemCache::Private
{
public:
    ~Private() {
        memcached_free(mc);
    }
    QList<QByteArray> servers;
    QThreadPool pool;
    memcached_st *mc;
};

MemCache::MemCache(int threads)
        : d(new Private())
{
    if (threads == -1)
        threads = QThread::idealThreadCount();
    d->pool.setMaxThreadCount(threads);
    d->mc = memcached_create(NULL);
}

void MemCache::setServers(const QList<QByteArray> &servers)
{
    int i;
    memcached_return err;
    d->servers = servers;
    foreach(QByteArray server, servers) {
        err = MEMCACHED_FAILURE;
        if (server.startsWith('/')) {
            err = memcached_server_add_unix_socket(d->mc, (server + '\0').constData());
        } else {
            i = server.lastIndexOf(':');
            err = memcached_server_add(d->mc, (server.left(i) += '\0').constData(), (server.right(server.size() - i - 1) + '\0').toUInt());
        }
        if (err != MEMCACHED_SUCCESS) {
            emit errorGot(QByteArray(memcached_strerror(d->mc, err)));
        }
    }
}

void MemCache::get(const QString &key)
{
    GetJob *job = new GetJob(d->mc, key);
    connect(job, SIGNAL(done(const QString&, const QByteArray&)),
            SIGNAL(valueGot(const QString&, const QByteArray&)),
            Qt::QueuedConnection);
    connect(job, SIGNAL(errorGot(const QString&)),
            SIGNAL(errorGot(const QString&)),
            Qt::QueuedConnection);
    d->pool.start(job);
}

MemCache::~MemCache()
{
}

void MemCache::set(const QString &key, const QByteArray &value)
{
    SetJob *job = new SetJob(d->mc, key, value);
    connect(job, SIGNAL(done(const QString&, const QByteArray&)),
            SIGNAL(valueSet(const QString&, const QByteArray&)));
    connect(job, SIGNAL(errorGot(const QString&)),
            SIGNAL(errorGot(const QString&)));
    d->pool.start(job);
}

void MemCache::del(const QString &key)
{
    DelJob *job = new DelJob(d->mc, key);
    connect(job, SIGNAL(done(const QString&)),
            SIGNAL(valueDel(const QString&)),
            Qt::QueuedConnection);
    connect(job, SIGNAL(errorGot(const QString&)),
            SIGNAL(errorGot(const QString&)),
            Qt::QueuedConnection);
    d->pool.start(job);
}
GetJob::GetJob(memcached_st *mc, const QString &key)
{
    this->mc = mc;
    this->key = key;
}

SetJob::SetJob(memcached_st *mc, const QString &key, const QByteArray &value)
{
    this->mc = mc;
    this->key = key;
    this->value = value;
}

DelJob::DelJob(memcached_st *mc, const QString &key)
{
    this->mc = mc;
    this->key = key;
}
void GetJob::run()
{
    char *buf;
    size_t value_length;
    uint32_t flags = 0;
    memcached_return err;
    buf = memcached_get(mc, key.toUtf8().constData(), key.toUtf8().size(), &value_length, &flags, &err);
    if (err == MEMCACHED_SUCCESS)
        emit done(key, QByteArray(buf, value_length));
    else
        emit errorGot(QString("Get Failed--%1--%2").arg(key).arg(memcached_strerror(mc, err)));
}

void SetJob::run()
{
    time_t expiration = 0;
    uint32_t flags = 0;
    memcached_return err;
    err = memcached_set(mc, key.toUtf8().constData(), key.toUtf8().size(), value.constData(), value.size(), expiration, flags);
    if (err == MEMCACHED_SUCCESS || err == MEMCACHED_BUFFERED)
        emit done(key, value);
    else
        emit errorGot(QString("Set Failed--%1--%2").arg(key).arg(memcached_strerror(mc, err)));
}

void DelJob::run()
{
    time_t expiration = 0;
    memcached_return err;
    err = memcached_delete(mc, key.toUtf8().constData(), key.toUtf8().size(), expiration);
    if (err == MEMCACHED_SUCCESS || err == MEMCACHED_BUFFERED)
        emit done(key);
    else
        emit errorGot(QString("Del Failed--%1--%2").arg(key).arg(memcached_strerror(mc, err)));
}

#include "memcache.moc"
